//--------------------------------------------------------------------------------
// This file is a portion of the D2D1Renderer.  It is distributed
// under the MIT License, available in the root of this distribution and 
// at the following URL:
//
// http://www.opensource.org/licenses/mit-license.php
//
// Copyright (c) Mirco Timmermann
//
//--------------------------------------------------------------------------------
#include "ScrollBar.h"

namespace D2 {

ScrollBar::ScrollBar(int x, int y, int width, int height, ScrollBarType type) : DisplayObject(x, y, width, height) {
	_type = type;

	_minValue = 0;
	_maxValue = 100;

	_leftValue = 0;
	_rightValue = 100;

	//
	_step = 0;
	
	_bar_width = 0;
	_bar_x = 0;
	_bar_height = 0;
	_bar_y = 0;

	_left_offset = 0;
	_right_offset = 0;

	_barPressed = false;
	_tempMouseDistance = 0;

	Refresh();
}

ScrollBar::~ScrollBar(void) {

}

/////////////////////////////DATA////////////////////////////
bool ScrollBar::Draw(Direct2DContext* ctx, D2D1::Matrix3x2F* mtx) {
	Draw_background(ctx);

	if(_type == ScrollBarType::VERTICAL) {
		DrawBar_vertical(ctx);
	} else {
		DrawBar_horizontal(ctx);
	}

	return true;
}

void ScrollBar::Draw_background(Direct2DContext* ctx) {
	//ctx->_disableCamera = true;

	//draw background
	ctx->SetFillStyle(0.3f, 0.3f, 0.3f);
	ctx->fillRect(0, 0, _width, _height);
        
	//calculate the rastered position.
	_left_offset = (_leftValue - _minValue) * _step;
	if(_left_offset < 0) _left_offset = 0;
        
	//calculate the rastered position.
	_right_offset = ((_rightValue - _minValue+1) * _step) - (_bar_height); //TODO: this._bar_height muss zu this._bar_width wenn vertical.
	if(_right_offset > 0) _right_offset = 0;

	//ctx->_disableCamera = false;
}

/*
*
*/
void ScrollBar::DrawBar_vertical(Direct2DContext* ctx) {
	//ctx->_disableCamera = true;

//bar
	ctx->SetFillStyle(0.7f, 0.7f, 0.7f);
  
	int new_bar_y = _left_offset + _bar_y;
    int new_bar_height = _bar_height - _left_offset + _right_offset;
            
	ctx->fillRect(_bar_x, new_bar_y, _bar_width, new_bar_height);
     
//grid
	ctx->lineWidth = 2;
	ctx->SetStrokeStyle(0.5f, 0.5f, 0.5f);

	ctx->beginPath();
        
	int grip_centerX = _bar_x + (_bar_width/2);
	int grip_centerY = new_bar_y + (new_bar_height/2);

	for(int i = -4; i <= 4; i+=4) {
		ctx->moveTo(grip_centerX - 4, grip_centerY+i);
		ctx->lineTo(grip_centerX + 4, grip_centerY+i);
	}
        
	ctx->stroke();

	ctx->closePath();
//

	//ctx->_disableCamera = false;
}

/*
*
*/
void ScrollBar::DrawBar_horizontal(Direct2DContext* ctx) {
	//ctx->_disableCamera = true;

//bar
	ctx->SetFillStyle(0.7f, 0.7f, 0.7f);
  
	int new_bar_x = _left_offset + _bar_x;
	int new_bar_width = _bar_width - _left_offset + _right_offset;
        
	ctx->fillRect(new_bar_x, _bar_y, new_bar_width, _bar_height);
       
//grid
	ctx->lineWidth = 2;
	ctx->SetStrokeStyle(0.5f, 0.5f, 0.5f);

	ctx->beginPath();
        
	int grip_centerX = new_bar_x + (new_bar_width/2);
    int grip_centerY = _bar_y + (_bar_height/2);

	for(int i = -4; i <= 4; i+=4) {
		ctx->moveTo(grip_centerX+i, grip_centerY - 4);
		ctx->lineTo(grip_centerX+i, grip_centerY + 4);
	}
        
	ctx->stroke();

	ctx->closePath();
//

	//ctx->_disableCamera = false;
}

void ScrollBar::OnMouseDown0(const MouseEvent &e) {
	D2D1_POINT_2F point = this->globalToLocal(e._stageX, e._stageY);
        
	if(HitTestBar(point.x, point.y)) {
		_barPressed = true;
	}
}

void ScrollBar::OnMouseUp0(const MouseEvent &e) {
	_barPressed = false;
    _tempMouseDistance = 0;
}

void ScrollBar::OnMouseLeave0(const MouseEvent &e) {
	_barPressed = false;
    _tempMouseDistance = 0;
}

void ScrollBar::OnMouseRaise0(const MouseEvent &e) {
	_barPressed = false;
    _tempMouseDistance = 0;
}

void ScrollBar::OnMouseDrag0(const MouseEvent &e) {
	if(_barPressed) {
		int mouseDistance = 0;
            
		if(_type == ScrollBarType::HORIZONTAL) { //horizontal
			mouseDistance = e._distanceX; //mouseState._distance.y;
		} else { //vertical
			mouseDistance = e._distanceY; //mouseState._distance.x;
		}

		_tempMouseDistance += mouseDistance;
            
		//raster-method.
		int i = GetBarOffsetByDistance(_tempMouseDistance);
		_leftValue += i;
		_rightValue += i;

		_tempMouseDistance = (_tempMouseDistance-_step*i);

		/*
		var event1 = { type:"LEFT_CHANGED", data:{toState:1, fromState:2}, target: this };
		dispatchEvent(event1);

		var event2 = { type:"RIGHT_CHANGED", data:{toState:1, fromState:2}, target: this };
		dispatchEvent(event2);

		var event3 = { type:"BAR_MOVED", data:{toState:1, fromState:2}, target: this };
		dispatchEvent(event3); 
		*/
	}
}

/*
*  calculate the time steps that is made with the mouse move.
*/
int ScrollBar::GetBarOffsetByDistance(float x) {
	//calculate the time steps that is made with the mouse move.
	//int i = Math::Round(x/_step);
	float value = x/_step;
	int i = value < 0.0 ? ceil(value - 0.5) : floor(value + 0.5); //Math::Round(x/_step);

	return ClampTimeDistance(i);
}

/*
* clamp the time if the right index or left index is reached. caluclates the missing index steps to the end. (the left index and the right index will not minimized by a fast move if it reached the end).
* caluclates the missing index steps to the end. (the left index and the right index will not minimized by a fast move if it reached the end).
*/
int ScrollBar::ClampTimeDistance(int i) {
	int diffMin = (i+_leftValue) - _minValue;
	if(diffMin < 0) i = i-diffMin;

	int diffMax = (i+_rightValue) - _maxValue;
	if(diffMax > 0) i = i-diffMax;

	return i;
}

/*
* checks if the position is inside the inner rectangle.
*/
bool ScrollBar::HitTestBar(int x, int y) {
	int new_bar_y = _left_offset + _bar_y;
	int new_bar_height = _bar_height - _left_offset + _right_offset;
        
	if(x > (_bar_x) && x < (_bar_x + _bar_width) && y > (new_bar_y) && y < (new_bar_y + new_bar_height)) {
		return true;
	}
	return false;
}

void ScrollBar::Refresh() {
	//calculate the visualisation.
	CalculateBarRectangle();
        
	//recalculate the size of the bar.
	CalculateStep();
}

void ScrollBar::CalculateStep() {
	if(_type == ScrollBarType::VERTICAL) {
		_step = (_bar_height)/((_maxValue - _minValue)*1.0f);
	} else {
		_step = (_bar_width)/((_maxValue - _minValue)*1.0f);
	}
}

/*
* calculates the inner rectangle position.
*/
void ScrollBar::CalculateBarRectangle() {
	int bar_width = _width; //*widthFactor;
    int bar_x = (_width - bar_width)/2;
    int bar_height = _height; //*heightFactor;
    int bar_y = (_height - bar_height)/2;
        
    _bar_width = bar_width;
    _bar_x = bar_x;
	_bar_height = bar_height;
	_bar_y = bar_y;
}

/*
* sets the min index.
*/
void ScrollBar::SetMinValue(int value) {
	_minValue = value;

	//prevent for min index goes before max index.
	if(_minValue >= _maxValue) _maxValue = _minValue+1;

	//clamp the maxIndex. (if the min index is bigger than the left index, the left index will be the maxIndex).
	if(_minValue > _rightValue) _rightValue = _minValue+1;
	if(_minValue > _leftValue) _leftValue = _minValue;

	CalculateStep();
}

/*
* sets the max index.
*/
void ScrollBar::SetMaxValue(int value) {
	_maxValue = value;

	//prevent for max index goes before max index.
	if(_maxValue <= _minValue) _minValue = _maxValue-1;

	//clamp the maxIndex. (if the max index is less than the rigth index, the right index will be the maxIndex).
	if(_maxValue < _leftValue) _leftValue = _maxValue-1;
	if(_maxValue < _rightValue) _rightValue = _maxValue;

	CalculateStep();
}

/*
* sets the left index. (left button position)
*/
void ScrollBar::SetLeftValue(int value) {
	_leftValue = value;
        
	//clamp
	if(_leftValue < _minValue) _minValue = _leftValue;
	if(_leftValue >= _rightValue) _rightValue = _leftValue+1;
	if(_leftValue > _maxValue) _maxValue = _leftValue+1;

	CalculateStep();
}

/*
* sets the right index. (right button position)
*/
void ScrollBar::SetRightValue(int value) {
	_rightValue = value;

	//clamp
	if(_rightValue > _maxValue) _maxValue = _rightValue;
	if(_rightValue <= _minValue) _minValue = _rightValue-1;
	if(_rightValue <= _leftValue) _leftValue = _rightValue-1;

	CalculateStep();
}

/*
* height -> default
*/
void ScrollBar::SetBarByHeight(int totalHeight, int height) {
	if(height == -1) height = 100; //TODO: //(_maxValue - _minValue);
        
	SetMaxValue(totalHeight);

	int offset = _rightValue - (_leftValue + height);
        
	if(totalHeight < height) {
		offset = 0;
		_leftValue = 0;
	}
        
	SetLeftValue(_leftValue + offset);
	SetRightValue(_leftValue + height);

	//var event3 = { type:"BAR_MOVED", data: { minValue: this.minValue, maxValue: this.maxValue, leftValue: this.leftValue, rightValue: this.rightValue }, target: this };
	//this.dispatchEvent(event3);
}

void ScrollBar::SetBarByRatio(float ratio) {
	if(_type == ScrollBarType::VERTICAL) {
		SetBarByHeight(_height*(1/ratio));
    } else {
		SetBarByHeight(_width*(1/ratio));
    }
}

}